package com.yifeng.repo.controller.excel.loader.worker;

import com.gomcarter.frameworks.base.common.AssertUtils;
import com.google.common.base.Strings;
import com.yifeng.repo.base.constant.BaseConstant;
import com.yifeng.repo.base.utils.converter.JacksonHelper;
import com.yifeng.repo.base.utils.redis.worker.RedisCacheWorker;
import com.yifeng.repo.base.utils.redis.worker.RedisLockWorker;
import com.yifeng.repo.controller.excel.configure.ExcelLoaderProperties;
import com.yifeng.repo.controller.excel.loader.dto.DataTaskDto;
import com.yifeng.repo.controller.excel.loader.dto.DataTaskTypeEnum;
import lombok.extern.slf4j.Slf4j;

import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

/**
 * Created by daibing on 2023/1/26.
 */
@Slf4j
public class ExcelLoaderRedisWorker {
    private final String applicationName;
    private final ExcelLoaderProperties properties;
    private final RedisCacheWorker redisCacheWorker;
    private final RedisLockWorker redisLockWorker;
    private final boolean nonRedis;

    /**
     * 支持没有redis的业务场景
     */
    public ExcelLoaderRedisWorker(String applicationName, ExcelLoaderProperties properties) {
        this.applicationName = applicationName;
        this.properties = properties;
        this.redisCacheWorker = null;
        this.redisLockWorker = null;
        this.nonRedis = true;
    }

    public ExcelLoaderRedisWorker(String applicationName, ExcelLoaderProperties properties,
                                  RedisCacheWorker redisCacheWorker, RedisLockWorker redisLockWorker) {
        this.applicationName = applicationName;
        this.properties = properties;
        this.redisCacheWorker = redisCacheWorker;
        this.redisLockWorker = redisLockWorker;
        this.nonRedis = false;
    }

    /**
     * redis空间规划：
     * 1、字符串：数据任务全局唯一锁，key是requestKey，value是OWNER_ID
     * 2、Long：并行任务数量，key是应用，value是数字
     * 2、字符串：key是applicationName+导入&导出类型+requestKey，value是DataTaskDto对象json字符串
     */

    public boolean lock(String requestKey, String lockerId, long expireMillis) {
        String keyByLock = getKeyByLock(requestKey);
        if (nonRedis) {
            log.info("lock is nothing to do, keyByLock {} return {}", keyByLock, true);
            return true;
        }
        AssertUtils.notNull(redisLockWorker, "未设置redisLockWorker客户端！");
        return redisLockWorker.lock(keyByLock, lockerId, expireMillis);
    }

    public String saveDataTask(DataTaskDto dataTaskDto) {
        String key = this.getKeyByTask(dataTaskDto.getRequestKey(), dataTaskDto.getType());
        String json = JacksonHelper.toJson(dataTaskDto);
        if (nonRedis) {
            log.info("saveDataTask is nothing to do, return key={};value={}", key, json);
            return String.format("key=%s;value=%s", key, json);
        }
        AssertUtils.notNull(redisCacheWorker, "未设置redisCacheWorker客户端！");
        redisCacheWorker.put(key, properties.getRedisKeyTtlSeconds(), json);
        return String.format("key=%s;value=%s", key, json);
    }

    public DataTaskDto getDataTask(String requestKey, DataTaskTypeEnum type) {
        String key = this.getKeyByTask(requestKey, type);
        if (nonRedis) {
            log.info("getDataTask is nothing to do, key={} return null", key);
            return null;
        }
        AssertUtils.notNull(redisCacheWorker, "未设置redisCacheWorker客户端！");
        String dataTask = redisCacheWorker.get(key);
        if (Strings.isNullOrEmpty(dataTask)) {
            return null;
        }
        return JacksonHelper.toObj(dataTask, DataTaskDto.class);
    }

    public List<String> listDataTaskRequestKey(DataTaskTypeEnum type) {
        String taskKeyPrefix = this.getTaskKeyPrefixByType(type);
        if (nonRedis) {
            log.info("listDataTaskRequestKey is nothing to do, taskKeyPrefix={} return emptyList", taskKeyPrefix);
            return Collections.emptyList();
        }
        AssertUtils.notNull(redisCacheWorker, "未设置redisCacheWorker客户端！");
        List<String> taskKeys = redisCacheWorker.scan(taskKeyPrefix.concat("*"), 100, 200);
        return taskKeys.stream().map(s -> s.replace(taskKeyPrefix, "")).collect(Collectors.toList());
    }

    public long incrRunningNumber() {
        String appRunningNumberKey = this.getKeyByAppRunningNumber();
        if (nonRedis) {
            log.info("incrRunningNumber is nothing to do, appRunningNumberKey={} return 0L", appRunningNumberKey);
            return 0L;
        }
        AssertUtils.notNull(redisCacheWorker, "未设置redisCacheWorker客户端！");
        return redisCacheWorker.incrBy(appRunningNumberKey, 1L);
    }

    public long decrRunningNumber() {
        String appRunningNumberKey = this.getKeyByAppRunningNumber();
        if (nonRedis) {
            log.info("decrRunningNumber is nothing to do, appRunningNumberKey={} return 0L", appRunningNumberKey);
            return 0L;
        }
        AssertUtils.notNull(redisCacheWorker, "未设置redisCacheWorker客户端！");
        return redisCacheWorker.decrBy(appRunningNumberKey, 1L);
    }

    public long getRunningNumber() {
        String appRunningNumberKey = this.getKeyByAppRunningNumber();
        if (nonRedis) {
            log.info("getRunningNumber is nothing to do, appRunningNumberKey={} return 0L", appRunningNumberKey);
            return 0L;
        }
        AssertUtils.notNull(redisCacheWorker, "未设置redisCacheWorker客户端！");
        String value = redisCacheWorker.get(appRunningNumberKey);
        if (Strings.isNullOrEmpty(value)) {
            return 0;
        }
        return Integer.parseInt(value);
    }

    public long clearRunningNumber() {
        String appRunningNumberKey = this.getKeyByAppRunningNumber();
        if (nonRedis) {
            log.info("clearRunningNumber is nothing to do, appRunningNumberKey={} return 0L", appRunningNumberKey);
            return 0L;
        }
        AssertUtils.notNull(redisCacheWorker, "未设置redisCacheWorker客户端！");
        String value = redisCacheWorker.get(appRunningNumberKey);
        if(Strings.isNullOrEmpty(value)) {
            return 0L;
        }
        redisCacheWorker.remove(appRunningNumberKey);
        return Long.parseLong(value);
    }

    /**
     * 获取数据任务全局唯一锁的key
     */
    private String getKeyByLock(String requestKey) {
        return properties.getRedisKeyPrefix().concat(applicationName).concat(BaseConstant.REDIS_SEPARATOR).concat(requestKey);
    }

    /**
     * 获取任务类型对应的key前缀
     */
    private String getTaskKeyPrefixByType(DataTaskTypeEnum type) {
        return properties.getRedisKeyPrefix().concat(applicationName)
                .concat(BaseConstant.REDIS_SEPARATOR).concat(type.name().toLowerCase())
                .concat(BaseConstant.REDIS_SEPARATOR);
    }

    /**
     * 获取应用运行任务数量的key
     */
    private String getKeyByAppRunningNumber() {
        return properties.getRedisKeyPrefix().concat(applicationName)
                .concat(BaseConstant.REDIS_SEPARATOR).concat("RunningNumber");
    }

    /**
     * 获取应用任务级别对应的key
     */
    private String getKeyByTask(String requestKey, DataTaskTypeEnum type) {
        return properties.getRedisKeyPrefix().concat(applicationName)
                .concat(BaseConstant.REDIS_SEPARATOR).concat(type.name().toLowerCase())
                .concat(BaseConstant.REDIS_SEPARATOR).concat(requestKey);
    }

}
